﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using SdmxMl.Common;
using SdmxMl.Helper;
using SdmxMl.Manager;


namespace SdmxMl.Structure
{
    /// <summary>
    /// ObjectRefType contains a set of reference fields for the purpose of referencing any object.
    /// This cannot be a local reference, therefore the agency identifier is required. It is also
    /// required that the class and package be supplied for the referenced object such that a
    /// complete URN reference can be built from the values provided. 
    /// Note that this is not capable of fully validating that all necessary fields are supplied for
    /// a given object type.
    /// </summary>
    public class ObjectRefType
    {
        /// <summary>
        /// The maintainableParentID attribute identifies the maintainable object in which the referenced
        /// object is defined, if applicable (maintainable-parent-object-id in the URN structure).
        /// This is only used in references where the referenced object is not itself maintainable.
        /// </summary>
        private string maintainableParentID;

        /// <summary>
        /// The maintainableParentVersion attribute identifies the version of the maintainable object
        /// in which the referenced object is defined (maintainable-parent-object-version in the URN
        /// structure). This is only used in references where the referenced object is not itself
        /// maintainable. This should only be used when the maintainableParentID is present.
        /// If this is available, a default of 1.0 will always apply.
        /// </summary>
        private string maintainableParentVersion;

        /// <summary>
        /// he containerID attribute identifies the object within a maintainable object in which the
        /// referenced object is defined (container-object-id in the URN structure). This is only used
        /// in references where the referenced object is not contained directly within a maintainable
        /// object (e.g. a Component within a ComponentList, within a maintainable Structure).
        /// If the container has a fixed identifier, this attribute will not be present.
        /// </summary>
        public string ContainerID { get; set; }

        private string id;
        private string version;
        private string agencyId;

        public string SourceUrn { get; set; }

        /// <summary>
        /// The class attribute indicates the class name of the object being referenced.
        /// This attribute allows any reference to be processed generically from this definition.
        /// References derived from this should fix the value of this attribute to indicate the type
        /// of object that is being referenced, or in the case that a reference which allows specific
        /// types of fields, the representation should be sub-setted to the appropriate values.
        /// </summary>
        private ObjectTypeCodelistType srcClass;

        /// <summary>
        /// The package attribute indicates the package name for the object being referenced.
        /// This attribute allows any reference to be processed generically from this definition.
        /// References derived from this should fix the value of this attribute to indicate the type
        /// of object that is being referenced, or in the case that a reference which allows specific
        /// types of fields, the representation should be sub-setted to the appropriate values.
        /// </summary>
        private PackageTypeCodelistType package;


        public ObjectRefType()
        {
            SourceUrn = string.Empty;
        }

        /// <summary> Parametrized C'tor from XML document </summary>
        /// <param name="nodeArtefact">the Source node</param>
        /// <param name="nsmgr">associated namespace manager</param>
        public ObjectRefType(XmlNode nodeArtefact, SdmxNamespaceManager nsmgr)
        {
            // Only for SDMX 2.1
            if (nsmgr.VersionSdmx == SdmxVersion.SDMX_20)
                throw new ApplicationException("Categorization not supported in SDMX 2.0");


            // Is source given by URN ?
            XmlNode ndUrn = nodeArtefact.SelectSingleNode("./URN", nsmgr);
            if (ndUrn != null)
            {
                SourceUrn = ndUrn.InnerText;
            }
            else
            {
                // Is source given via full Reference
                XmlNode ndRef = nodeArtefact.SelectSingleNode("./Ref", nsmgr);
                if (ndRef != null)
                {
                    maintainableParentID = Xmlhelper.GetAtt(ndRef, "maintainableParentID");
                    agencyId = Xmlhelper.GetAtt(ndRef, "agencyID");
                    id = Xmlhelper.GetAtt(ndRef, "id");
                    maintainableParentVersion = Xmlhelper.GetAtt(ndRef, "maintainableParentVersion");
                    if (string.IsNullOrEmpty(maintainableParentVersion))
                        maintainableParentVersion = "1.0";
                    version = Xmlhelper.GetAtt(ndRef, "version");
                    if (string.IsNullOrEmpty(version))
                        version = "1.0";

                    // Class required
                    string str = Xmlhelper.GetAtt(ndRef, "class");
                    if (str.Length > 0)
                    {
                        if (Enum.IsDefined(typeof(ObjectTypeCodelistType), str))
                            srcClass = (ObjectTypeCodelistType)Enum.Parse(typeof(ObjectTypeCodelistType), str, true);
                        else
                            throw new ApplicationException("Unrecognised class reference: " + str);
                    }
                    else
                        throw new ApplicationException("Missing class reference");

                    // Package required
                    str = Xmlhelper.GetAtt(ndRef, "package");
                    if (str.Length > 0)
                    {
                        if (Enum.IsDefined(typeof(PackageTypeCodelistType), str))
                            package = (PackageTypeCodelistType)Enum.Parse(typeof(PackageTypeCodelistType), str, true);
                        else
                            throw new ApplicationException("Unrecognised package reference: " + str);
                    }
                    else
                        throw new ApplicationException("Missing package reference");

                    // Now create URN
                    StringBuilder sb = new StringBuilder(UrnManager.UrnBase);
                    sb.Append('.');
                    sb.Append(package.ToString()).Append('.');
                    sb.Append(srcClass.ToString()).Append('=');
                    sb.Append(agencyId).Append(':');


                    if (string.IsNullOrEmpty(maintainableParentID) == false)
                    {
                        sb.Append(maintainableParentID).Append('(').Append(maintainableParentVersion).Append(')');
                        sb.Append('.').Append(id);
                    }
                    else
                    {
                        sb.Append(id).Append('(').Append(version).Append(')');
                    }

                    SourceUrn = sb.ToString();
                }
            }

            


        }

    }
}
